package com.hhkj.admin.common.aspectj;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.hhkj.admin.common.annotation.Idempotent;
import com.hhkj.api.common.session.holder.ApiStudentSessionHolder;
import com.hhkj.api.sale.common.session.holder.ApiSaleSessionHolder;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.login.api.ApiSessionHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class IdempotentAspect {

    private static final Logger logger = LoggerFactory.getLogger(IdempotentAspect.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private final String LOCK_VALUE = "locked";

    private final String LOCK_KEY_PREFIX = "idempotent";

    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Exception {
        logger.info("幂等性接口");

        //获取注解信息
        Idempotent.UserTypeCode userTypeCode = idempotent.userTypeCode();
        boolean autoRelease = idempotent.autoRelease();
        long timeout = idempotent.timeout();
        String customizeSuffix = idempotent.customizeSuffix();

        //获取方法信息
        String clazz = joinPoint.getTarget().getClass().getSimpleName();
        String method = joinPoint.getSignature().getName();

        //获取redis key
        String lockKey = getLockKey(clazz, method, userTypeCode, customizeSuffix);
        if (lock(lockKey, timeout) == false) {
            throw new GlobalException("请求处理中，请稍后再试");
        }
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            throw e;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            if (autoRelease) {
                unLock(lockKey);
            }
        }
    }

    private boolean lock(String lockKey, long timeout) {
        return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, LOCK_VALUE, timeout, TimeUnit.SECONDS);
    }

    private boolean unLock(String lockKey) {
        return stringRedisTemplate.delete(lockKey);
    }

    private Long getLoginUserId(Idempotent.UserTypeCode userTypeCode) {
        Long id = null;
        if (Idempotent.UserTypeCode.SALE.equals(userTypeCode)) {
            id = ApiSaleSessionHolder.getSaleId();
        } else if (Idempotent.UserTypeCode.SYS_USER.equals(userTypeCode)) {
            id = SecurityUtils.getLoginUser().getUserId();
        } else if (Idempotent.UserTypeCode.STUDENT.equals(userTypeCode)) {
            id = ApiStudentSessionHolder.getStudentId();
        } else {
            id = ApiSessionHolder.getSessionObjId();
        }
        if (ObjectUtil.isEmpty(id)) {
            logger.info("没有已经登录的账号");
        }
        return id;
    }

    private String getLockKey(String clazz, String method, Idempotent.UserTypeCode userTypeCode, String customizeSuffix) {
        String lockKey = LOCK_KEY_PREFIX + StrUtil.COLON + clazz + StrUtil.COLON + method;
        Long loginUserId = getLoginUserId(userTypeCode);
        if (ObjectUtil.isNotEmpty(loginUserId)) {
            lockKey = lockKey + StrUtil.COLON + loginUserId;
        }
        if (StringUtils.isNotBlank(customizeSuffix)) {
            lockKey = lockKey + StrUtil.COLON + customizeSuffix;
        }
        return lockKey;
    }

}
